#ifndef AVALON_SERVICECHANNELFACTORY_HPP
#define AVALON_SERVICECHANNELFACTORY_HPP
#include <deque>
#include <map>
#include <string>
#include <boost/function.hpp>
#include "Avalon/Base/Handle.hpp"
#include "Avalon/Codecs/Codecs.hpp"
#include "Avalon/IO/IO.hpp"
#include "Avalon/Serialization/Serialization.hpp"
#include "Avalon/ServiceLocator/ServiceLocator.hpp"

namespace Avalon {
namespace ServiceLocator {

  /*! \class ServiceChannelBuilder
   *  \brief Service provided to a builder for creating sub-components.
   */
  class ServiceChannelBuilder {
    public:

      //! Constructs a ServiceChannelBuilder.
      /*!
        \param serviceChannelFactory The ServiceChannelFactory containing the
                                     component builders.
      */
      ServiceChannelBuilder(ServiceChannelFactory* serviceChannelFactory);

      //! Builds an Encoder from a JSON node.
      /*!
        \param encoderNode A JSON node describing the Encoder to create.
        \return The Encoder represented by the <i>encoderNode</i>.
      */
      Carry<Codecs::Encoder> BuildEncoder(const std::string& encoderNode);

      //! Builds a Decoder from a JSON node.
      /*!
        \param decoderNode A JSON node describing the Decoder to create.
        \return The Decoder represented by the <i>decoderNode</i>.
      */
      Carry<Codecs::Decoder> BuildDecoder(const std::string& decoderNode);

      //! Builds a Connection from a JSON node.
      /*!
        \param connectionNode A JSON node describing the Connection to create.
        \return The Connection represented by the <i>connectionNode</i>.
      */
      Carry<IO::Connection> BuildConnection(const std::string& connectionNode);

      //! Builds a Reader from a JSON node.
      /*!
        \param readerNode A JSON node describing the Reader to create.
        \return The Reader represented by the <i>readerNode</i>.
      */
      Carry<IO::Reader> BuildReader(const std::string& readerNode);

      //! Builds a Writer from a JSON node.
      /*!
        \param writerNode A JSON node describing the Writer to create.
        \return The Writer represented by the <i>writerNode</i>.
      */
      Carry<IO::Writer> BuildWriter(const std::string& writerNode);

      //! Builds a Channel from a JSON node.
      /*!
        \param channelNode A JSON node describing the Channel to create.
        \return The Channel represented by the <i>channelNode</i>.
      */
      Carry<IO::Channel> BuildChannel(const std::string& channelNode);

      //! Builds a DataShuttleFactory from a JSON node.
      /*!
        \param shuttleNode A JSON node describing the DataShuttleFactory to
                           create.
        \return The DataShuttleFactory represented by the <i>shuttleNode<i>.
      */
      Carry<Serialization::DataShuttleFactory> BuildDataShuttleFactory(
        const std::string& shuttleNode);

      //! Pushes a Reader onto the build stack.
      /*!
        \param reader The Reader to push onto the stack.
      */
      void Push(Carry<IO::Reader> reader);

      //! Pops a Reader from the build stack.
      void PopReader();

      //! Pushes a Writer onto the build stack.
      /*!
        \param writer The Writer to push onto the stack.
      */
      void Push(Carry<IO::Writer> writer);

      //! Pops a Writer from the build stack.
      void PopWriter();

      //! Returns the underlying ServiceChannelFactory.
      const ServiceChannelFactory& GetServiceChannelFactory();

    private:
      friend class ServiceChannelFactory;
      ServiceChannelFactory* m_serviceChannelFactory;
      std::deque<Carry<Codecs::Encoder> > m_encoderStack;
      std::deque<Carry<Codecs::Decoder> > m_decoderStack;
      std::deque<Carry<IO::Connection> > m_connectionStack;
      std::deque<Carry<IO::Reader> > m_readerStack;
      std::deque<Carry<IO::Writer> > m_writerStack;
      std::deque<Carry<IO::Channel> > m_channelStack;
      std::deque<Carry<Serialization::DataShuttleFactory> >
        m_dataShuttleFactoryStack;
  };

  /*! \class ServiceChannelFactory
   *  \brief Factory for creating a Channel to a service.
   */
  class ServiceChannelFactory {
    public:

      //! Defines a function that builds an Encoder.
      typedef boost::function<Carry<Codecs::Encoder> (ServiceChannelBuilder,
        const std::string&)> EncoderBuilder;

      //! Defines a function that builds a Decoder.
      typedef boost::function<Carry<Codecs::Decoder> (ServiceChannelBuilder,
        const std::string&)> DecoderBuilder;

      //! Defines a function that builds a Connection.
      typedef boost::function<Carry<IO::Connection> (ServiceChannelBuilder,
        const std::string&)> ConnectionBuilder;

      //! Defines a function that builds a Reader.
      typedef boost::function<Carry<IO::Reader> (ServiceChannelBuilder,
        const std::string&)> ReaderBuilder;

      //! Defines a function that builds a Writer.
      typedef boost::function<Carry<IO::Writer> (ServiceChannelBuilder,
        const std::string&)> WriterBuilder;

      //! Defines a function that builds a Channel.
      typedef boost::function<Carry<IO::Channel> (ServiceChannelBuilder,
        const std::string&)> ChannelBuilder;

      //! Defines a function that builds a DataShuttleFactory.
      typedef boost::function<Carry<Serialization::DataShuttleFactory> (
        ServiceChannelBuilder, const std::string&)> DataShuttleFactoryBuilder;

      //! Returns the default EncoderBuilders.
      static std::map<std::string, EncoderBuilder> GetDefaultEncoderBuilders();

      //! Returns the default DecoderBuilders.
      static std::map<std::string, DecoderBuilder> GetDefaultDecoderBuilders();

      //! Returns the default ConnectionBuilders.
      static std::map<std::string, ConnectionBuilder>
        GetDefaultConnectionBuilders();

      //! Returns the default ReaderBuilders.
      static std::map<std::string, ReaderBuilder> GetDefaultReaderBuilders();

      //! Returns the default WriterBuilders.
      static std::map<std::string, WriterBuilder> GetDefaultWriterBuilders();

      //! Returns the default ChannelBuilders.
      static std::map<std::string, ChannelBuilder> GetDefaultChannelBuilders();

      //! Returns the default DataShuffleFactoryBuilders.
      static std::map<std::string, DataShuttleFactoryBuilder>
        GetDefaultDataShuttleFactoryBuilders();

      //! Constructs a ServiceChannelFactory with default builders.
      ServiceChannelFactory();

      //! Constructs a ServiceChannelFactory.
      /*!
        \param encoders A mapping from type to EncoderBuilders.
        \param decoders A mapping from type to DecoderBuilders.
        \param connections A mapping from type to ConnectionBuilders. 
        \param readers A mapping from type to ReaderBuilders. 
        \param writers A mapping from type to WriterBuilders.
        \param channels A mapping from type to ChannelBuilders.
        \param dataShuttleFactories A mapping from type to
                                    DataShuttleFactoryBuilders.
      */
      ServiceChannelFactory(
        const std::map<std::string, EncoderBuilder>& encoders,
        const std::map<std::string, DecoderBuilder>& decoders,
        const std::map<std::string, ConnectionBuilder>& connections,
        const std::map<std::string, ReaderBuilder>& readers,
        const std::map<std::string, WriterBuilder>& writers,
        const std::map<std::string, ChannelBuilder>& channels,
        const std::map<std::string, DataShuttleFactoryBuilder>&
        dataShuttleFactories);

      ~ServiceChannelFactory();

      //! Creates an Encoder from a JSON node.
      /*!
        \param encoderNode A JSON node describing the Encoder to create.
        \return The Encoder represented by the <i>encoderNode</i>.
      */
      Codecs::Encoder* CreateEncoder(const std::string& encoderNode);

      //! Creates a Decoder from a JSON node.
      /*!
        \param decoderNode A JSON node describing the Decoder to create.
        \return The Decoder represented by the <i>decoderNode</i>.
      */
      Codecs::Decoder* CreateDecoder(const std::string& decoderNode);

      //! Creates a Connection from a JSON node.
      /*!
        \param connectionNode A JSON node describing the Connection to create.
        \return The Connection represented by the <i>connectionNode</i>.
      */
      IO::Connection* CreateConnection(const std::string& connectionNode);

      //! Creates a Reader from a JSON node.
      /*!
        \param readerNode A JSON node describing the Reader to create.
        \return The Reader represented by the <i>readerNode</i>.
      */
      IO::Reader* CreateReader(const std::string& readerNode);

      //! Creates a Writer from a JSON node.
      /*!
        \param writerNode A JSON node describing the Writer to create.
        \return The Writer represented by the <i>writerNode</i>.
      */
      IO::Writer* CreateWriter(const std::string& writerNode);

      //! Creates a Channel from a JSON node.
      /*!
        \param channelNode A JSON node describing the Channel to create.
        \return The Channel represented by the <i>channelNode</i>.
      */
      IO::Channel* CreateChannel(const std::string& channelNode);

      //! Creates a DataShuttleFactory from a JSON node.
      /*!
        \param shuttleNode A JSON node describing the DataShuttleFactory to
                           create.
        \return The DataShuttleFactory represented by the <i>shuttleNode<i>.
      */
      Serialization::DataShuttleFactory* CreateDataShuttleFactory(
        const std::string& shuttleNode);

    private:
      friend class ServiceChannelBuilder;
      template<typename T>
      struct Component {
        typedef boost::function<Carry<T> (ServiceChannelBuilder,
          const std::string&)> Builder;
        std::map<std::string, Builder> m_builders;

        Component(const std::map<std::string, Builder>& builders);
        Carry<T> Create(ServiceChannelBuilder builder, const std::string& node,
          std::deque<Carry<T> >* stack);
      };
      Component<Codecs::Encoder> m_encoders;
      Component<Codecs::Decoder> m_decoders;
      Component<IO::Connection> m_connections;
      Component<IO::Reader> m_readers;
      Component<IO::Writer> m_writers;
      Component<IO::Channel> m_channels;
      Component<Serialization::DataShuttleFactory> m_dataShuttleFactories;
  };
}
}

#endif // AVALON_SERVICECHANNELFACTORY_HPP
